package com.apes.fico.engine;

import com.alibaba.fastjson.JSON;
import com.apes.fico.engine.annotation.AccountingEngineOneToOne;
import com.apes.fico.engine.model.*;
import com.apes.fico.engine.repository.BusinessDocumentRepository;
import com.apes.fico.engine.repository.ElementChartRepository;
import com.apes.fico.engine.repository.TempVoucherRepository;
import com.apes.fico.engine.repository.VoucherTemplateRepository;
import com.apes.fico.masterdata.gl.model.AccountingModel;
import com.apes.fico.masterdata.gl.model.AccountingOperatingChart;
import com.apes.fico.masterdata.gl.model.AccountingSystem;
import com.apes.fico.masterdata.gl.model.CashFlow;
import com.apes.fico.masterdata.gl.service.AccountingChartDistributionCompanyService;
import com.apes.fico.masterdata.gl.service.AccountingOperatingChartService;
import com.apes.framework.api.DomainService;
import com.apes.framework.api.SpringManager;
import com.apes.framework.jpa.ApesBean;
import com.apes.framework.jpa.repository.CustomRepository;
import com.apes.framework.jpa.spec.JpaDsl;
import com.apes.framework.plugin.esb.api.SimpleRequest;
import com.apes.framework.util.*;
import com.apes.scm.masterdata.party.model.PartyRole;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.apes.framework.util.ReflectUtil.getMerge;
import static java.util.stream.Collectors.groupingBy;

/**
 * 功能：会计凭证引擎实现
 *
 * @author xul
 * @create 2019-01-24 14:01
 */
@Service("accountingEngine")
public class AccountingEngineImpl extends DomainService implements AccountingEngine {
    protected final Logger logger = Logger.getLogger(getClass());
    private final String m = "凭证模板：%s-%s 执行表达式：%s 错误！\n";

    @Autowired
    private BusinessDocumentRepository businessDocumentRepository;

    @Autowired
    private TempVoucherRepository tempVoucherRepository;

    /**
     * 订阅业务变更事件
     */
    public void subscribe(SimpleRequest request) {
        Object entity = request.getData();
        if (entity instanceof ApesBean) {
            intercept((ApesBean) entity);
        }
    }

    /**
     * 业务数据拦截
     */
    @Override
    public void intercept(ApesBean entity) {
        /*业务凭证数据过滤，选择有效凭证模板*/
        Collection<VoucherTemplate> voucherTemplates = getVoucherTemplates(entity);
        if (voucherTemplates == null || voucherTemplates.isEmpty()) return;
        String businessType = FicoUtil.getNodeName(entity);
        String businessId = JpaUtil.getIdToStr(entity);
        List<BusinessDocument> documents = voucherTemplates.stream().map(vt -> {
            BusinessDocument doc = new BusinessDocument();
            doc.setVoucherTemplate(vt);
            doc.setBusinessType(businessType);
            doc.setBusinessId(businessId);
            return doc;
        }).collect(Collectors.toList());

        /*业务凭证保存*/
        documents = businessDocumentRepository.save(documents);

        /*凭证生成（异步）*/
        documents.forEach(this::execute);
    }

    @Autowired
    private VoucherTemplateRepository voucherTemplateRepository;

    /**
     * 业务凭证数据过滤，选择有效凭证模板
     **/
    private Collection<VoucherTemplate> getVoucherTemplates(ApesBean entity) {
        PartyRole company = FicoUtil.getCompany(entity);
        String businessTypeId = entity.getClass().getSimpleName();
        Collection<VoucherTemplate> voucherTemplates = voucherTemplateRepository.findAllByBusinessType_idOrderByIdAsc(businessTypeId);  /*待优化*/
        Stream<VoucherTemplate> stream = voucherTemplates.stream().filter(vt -> {
            /*过滤无效凭证模板*/
            if (!vt.isValid()) return false;

            /*按业务类型过滤无效凭证模板**/
            String businessType = FicoUtil.getNodeName(entity);
            if (!vt.getBusinessType().getId().equals(businessType)) return false;

            /*满足订阅条件**/
            String message = String.format(m, vt.getId(), "", vt.getSubscription());
            if (!((boolean) FicoUtil.eval(vt.getSubscription(), getMerge(entity), true, e -> message + e.getMessage())))
                return false;

            if (vt.getCompanyField() == null) {
                /*相同科目体系*/
                if (!company.isFin()) return false;
                AccountingSystem accountingSystem = getAccountingSystem(company);

                if (!vt.getAccountingSystem().equals(accountingSystem)) return false;
            } else {
                /*相同科目体系*/
                String companyField = vt.getCompanyField();
                PartyRole localCompany = FicoUtil.getCompany(entity, companyField);
                if (!localCompany.isFin()) return false;
                AccountingSystem accountingSystem = getAccountingSystem(localCompany);
                if (!vt.getAccountingSystem().equals(accountingSystem)) return false;
            }


            /*幂等控制*/
            String businessId = JpaUtil.getIdToStr(entity);
            Map filters = MapUtil.mapper("businessType", businessType, "businessId", businessId, "voucherTemplate", vt);
            return businessDocumentRepository.findAll(JpaDsl.toCriteria(filters)).size() <= 0;
        });
        List<VoucherTemplate> vts = stream.collect(Collectors.toList());
        if (vts.isEmpty()) return null;
        Collection<VoucherTemplate> result = new ArrayList<>();
        /*相同模板组，只取一个模板*/
        vts.stream().collect(groupingBy(VoucherTemplate::getVoucherTemplateGroup)).forEach((vtg, templates) -> {
            /*优先取公司凭证模板*/
            VoucherTemplate template = templates.stream().filter(vt -> {
                if (vt.getCompanyField() == null) {
                    return vt.getCompany() != null && vt.getCompany().equals(company);
                } else {/*A结转B的数据*/
                    /*相同科目体系*/
                    String companyField = vt.getCompanyField();
                    PartyRole localCompany = FicoUtil.getCompany(entity, companyField);
                    return vt.getCompany() != null && vt.getCompany().equals(localCompany);
                }
            }).findAny().orElse(null);
            /*如果没有定义公司凭证模板，则取公共凭证模板*/
            if (template == null) {
                template = templates.stream().filter(vt -> vt.getCompany() == null).findAny().orElse(null);
            }
            if (template == null) {
                throw new RuntimeException("公司：" + company.getName() + " 没有定义凭证模板：" + vtg.getName() + "！");
            }
            result.add(template);
        });
        return result;
    }

    /**
     * 功能：处理异常消息
     **/
    public void handlingException(SimpleRequest request) {
        BusinessDocument document = SpringManager.getBean(BusinessDocumentRepository.class).
                findOne(JpaDsl.toCriteriaByEq("state", "unusual", "id", request.get("id"))).orElse(null);
        if (document != null) {
            execute(document);
        }
    }

    /**
     * 生成原始会计凭证
     */
    @Override
    public void execute(BusinessDocument document) {
        TempVoucher tv = null;
        try {
            tv = createVoucher(document);
        } catch (Exception e) {
            e.printStackTrace();
            document.setState("unusual");
            document.setMessage(e.getMessage());
            businessDocumentRepository.save(document);
        }
        if (tv != null) save(tv);
    }

    /**
     * 保存凭证
     **/
    public TempVoucher save(TempVoucher tv) {
        checkVoucher(tv);
        tv = tempVoucherRepository.save(tv);

        Optional<BusinessDocument> document = businessDocumentRepository.findOne(JpaDsl.toCriteriaByEq(
                "voucherTemplate", tv.getVoucherTemplate(),
                "businessType", tv.getBusinessType(),
                "businessId", tv.getBusinessId()));
        if (!document.isPresent()) throw new RuntimeException("业务凭证对象不存在！");
        BusinessDocument document1 = document.get();
        document1.setState("generated");
        document1.setPostDate(tv.getPostDate());
        document1.setMessage("凭证生成成功！" + tv.getId());
        businessDocumentRepository.save(document1);
        /*测试用正式库要屏蔽*/
        invoke("finance.GLVoucher.manualVoucher", JSON.toJSONString(MapUtil.mapped(tv)));
        return tv;
    }

    /**
     * 创建凭证
     **/
    public TempVoucher createVoucher(BusinessDocument document) {
        ApesBean entity = getEntity(document);
        PartyRole company = FicoUtil.getCompany(entity);

        String companyField = document.getVoucherTemplate().getCompanyField();
        if (companyField != null) {
            company = FicoUtil.getCompany(entity, companyField);
        }

        /*分录创建*/
        List<TempVoucherItem> tempVoucherItems = createVoucherItems(document.getVoucherTemplate(), entity, company, null);
        if (CollectionUtils.isEmpty(tempVoucherItems)) {
            String message = String.format("凭证创建失败，没有分录明细！[凭证模板：%s] [业务凭证：%s %s]",
                    document.getVoucherTemplate().getId(), document.getBusinessType(), document.getBusinessId());

            //throw new RuntimeException(message);
            return null;
        }

        /*分录合并*/
        List<TempVoucherItem> voucherItems = new ArrayList<>();
        tempVoucherItems.stream().collect(groupingBy(TempVoucherItem::merge)).forEach((merge, tvs) -> {
            TempVoucherItem tvi = new TempVoucherItem();
            tvi.setCollectAux(merge.getCollectAux());
            tvi.setSummary(merge.getSummary());
            tvi.setVoucherTemplateEntry(merge.getVoucherTemplateEntry());
            tvi.setAccountingOperatingChart(merge.getAccountingOperatingChart());
            tvi.setDebit(tvs.stream().map(TempVoucherItem::getDebit).reduce(Arith::add).orElse(0.0));
            tvi.setCredit(tvs.stream().map(TempVoucherItem::getCredit).reduce(Arith::add).orElse(0.0));
            tvi.setVoucherAuxiliaryList(tvs.get(0).getVoucherAuxiliaryList());
            tvi.setCashFlow(merge.getCashFlow());
            voucherItems.add(tvi);
        });

        /*分录排序*/
        voucherItems.sort(Comparator.comparing(vi -> vi.getVoucherTemplateEntry().getSequence()));

        /*创建凭证*/
        return createTempVoucher(document, company, getMerge(entity), voucherItems);
    }

    /**
     * 凭证有效性检查
     **/
    private void checkVoucher(TempVoucher tv) {
        /*检查借贷金额*/
        double debit = tv.getVoucherItemList().stream().map(TempVoucherItem::getDebit).reduce(Arith::add).orElse(0.0);
        double credit = tv.getVoucherItemList().stream().map(TempVoucherItem::getCredit).reduce(Arith::add).orElse(0.0);
        long lCount = tv.getVoucherItemList().stream().filter((TempVoucherItem item) -> item.getDebit() != 0 || item.getCredit() != 0).count();
        if (Arith.compare(Arith.round(credit, 4), Arith.round(debit, 4), 3) != 0) {
            throw new RuntimeException("分录借贷不平！\n" + JSON.toJSONString(MapUtil.mapped(tv)));
        }
        if (Arith.compare(Arith.round(tv.getDebitTotal(), 4), Arith.round(tv.getCreditTotal(), 4), 3) != 0) {
            throw new RuntimeException("凭证借贷不平！\n" + JSON.toJSONString(MapUtil.mapped(tv)));
        }
        if (tv.getDebitTotal() == 0 && lCount == 0) {
            throw new RuntimeException("凭证金额为零！");
        }
    }

    /**
     * 创建凭证
     **/
    private TempVoucher createTempVoucher(BusinessDocument document, PartyRole company, Map<String, Object> context, List<TempVoucherItem> voucherItems) {
        VoucherTemplate vt = document.getVoucherTemplate();

        /*记账日期*/
        String express = vt.getDateField();
        Date postDate = FicoUtil.eval(express, context, true, e -> String.format(m, vt.getId(), "记账日期", express) + e.getMessage());

        /*创建汇总数据*/
        List<Map> dimensions = createDimensions(vt, context);
        /*创建汇总归集key*/
        String collectKey = FicoUtil.createCollectKey(dimensions, row -> (String) ((Map) row).get("id"));

        TempVoucher voucher = new TempVoucher();
        voucher.setCompany(company);
        voucher.setPostDate(postDate);
        voucher.setAccountingVoucherType(vt.getVoucherTemplateGroup().getAccountingVoucherType());
        voucher.setVoucherItemList(voucherItems);
        voucher.setDebitTotal(voucherItems.stream().map(TempVoucherItem::getDebit).reduce(Arith::add).orElse(0.0));
        voucher.setCreditTotal(voucherItems.stream().map(TempVoucherItem::getCredit).reduce(Arith::add).orElse(0.0));
        voucher.setBusinessType(document.getBusinessType());
        voucher.setBusinessId(document.getBusinessId());
        voucher.setVoucherTemplate(document.getVoucherTemplate());
        if (!CollectionUtils.isEmpty(dimensions)) {
            voucher.setCollectValue(JSON.toJSONString(dimensions));
            voucher.setCollectKey(collectKey);
            StringBuilder sb = new StringBuilder();
            dimensions.forEach(m -> {
                String name = (String) m.get("name");
                if (name == null) name = (String) m.get("id");
                if (sb.length() > 0) sb.append(",");
                sb.append(name);
            });
            voucher.setRemark(sb.toString());
        }
        return voucher;
    }

    /**
     * 创建汇总数据
     **/
    private List<Map> createDimensions(final VoucherTemplate vt, final Map<String, Object> context) {
        String dimension = vt.getDimension();
        if (StringUtils.isEmpty(dimension)) return null;
        List<Map> dimensions = new ArrayList<>();
        for (String key : dimension.split(",")) {
            Object entity = FicoUtil.eval(key, context, null, false, Throwable::getMessage);
            if (entity == null) {
                throw new RuntimeException("凭证策略：" + key + " 值不存在！");
            }
            String businessName = null;
            String businessId;
            if (entity instanceof ApesBean) {
                businessId = JpaUtil.getIdToStr((ApesBean) entity);
                businessName = ReflectUtil.getFieldValue(entity, "name");
            } else {
                businessId = (String) entity;
            }
            dimensions.add(MapUtil.mapper("id", businessId, "name", businessName));
        }
        return dimensions;
    }

    /**
     * 创建凭证分录
     **/
    private List<TempVoucherItem> createVoucherItems(VoucherTemplate voucherTemplate, ApesBean entity, final PartyRole company, Map<String, Object> parent) {
        Map<String, Object> context = FicoUtil.createContext(entity, parent);
        List<TempVoucherItem> voucherItems = createVoucherItems(voucherTemplate, context, company);
        ReflectUtil.doWithFields(entity.getClass(), (descriptor) -> {
            if (Collection.class.isAssignableFrom(descriptor.getPropertyType())) {
                Collection<Object> entities = ReflectUtil.getFieldValue(entity, descriptor);
                if (CollectionUtils.isEmpty(entities)) return;
                entities.stream().filter(e -> ApesBean.class.isAssignableFrom(e.getClass()))
                        .forEach(e -> voucherItems.addAll(createVoucherItems(voucherTemplate, (ApesBean) JpaUtil.getImplementation(e), company, context)));
            } else if (ApesBean.class.isAssignableFrom(descriptor.getPropertyType())) {
                ApesBean e = ReflectUtil.getFieldValue(entity, descriptor);
                voucherItems.addAll(createVoucherItems(voucherTemplate, (ApesBean) JpaUtil.getImplementation(e), company, context));
            }
        }, (descriptor) -> {
            OneToMany oneToMany = ReflectUtil.getAnnotation(entity, descriptor.getName(), OneToMany.class);
            if (oneToMany != null) {
                return true;
            }
            AccountingEngineOneToOne oneToOne = ReflectUtil.getAnnotation(entity, descriptor.getName(), AccountingEngineOneToOne.class);
            if (oneToOne != null) {
                return true;
            }
            return false;
        });
        FicoUtil.removeContext(context);
        return voucherItems;
    }

    /**
     * 创建凭证分录
     **/
    private List<TempVoucherItem> createVoucherItems(final VoucherTemplate vt, final Map<String, Object> context, final PartyRole company) {
        AccountingSystem as = vt.getAccountingSystem();
        List<TempVoucherItem> voucherItems = new ArrayList<>();
        vt.getVoucherTemplateEntries().stream().filter(VoucherTemplateEntry::isValid)
                .collect(groupingBy(VoucherTemplateEntry::group))
                .forEach((g, vtes) -> { /*相同分录组，按优先级，只创建一条分录*/
                    vtes.sort(Comparator.comparing(VoucherTemplateEntry::getPriority));
                    for (VoucherTemplateEntry vte : vtes) {

                        /*执行条件公式，过滤无效分录*/
                        String conditionFormula = vte.getConditionFormula();
                        conditionFormula = StringUtils.isEmpty(conditionFormula) ? "1 == 1" : conditionFormula;
                        if (!FicoUtil.eval(conditionFormula, context, false, false, Throwable::getMessage)) continue;

                        /*计算发生金额*/
                        String message, express;
                        express = vte.getAmountFormula();
                        message = String.format(m, vt.getId(), vte.getId(), express);
                        Number n = FicoUtil.eval(express, context, 0.0, true, e -> message + e.getMessage());
                        double amount = n.doubleValue();
                        if (amount == 0) continue;

                        /*查找运营科目*/
                        AccountingOperatingChart aoc = getAccountingOperatingChart(company, as, vte, context);
                        if (aoc == null) continue;

                        /*现金流量项目*/
                        CashFlow cashFlow = null;
                        if (aoc.hasCashFlow()) {
                            cashFlow = vte.getCashFlow();
                            if (cashFlow == null) {
                                throw new RuntimeException("科目：" + aoc.getCode() + " 有现金流量项目，模板分录定义错误！" );
                            }
                        }

                        /*辅助核算*/
                        List<TempVoucherAuxiliary> tvas = createTempVoucherAuxiliaries(aoc, context, vte);
                        /*辅助核算归集key*/
                        String collectAux = FicoUtil.createCollectKey(tvas, tva -> ((TempVoucherAuxiliary) tva).getAccountingOperatingChartAuxiliary().getId() + "," + ((TempVoucherAuxiliary) tva).getCode());
                        /*摘要信息**/
                        String summary = null;
                        express = vte.getSummaryFormula();
                        if (!StringUtils.isEmpty(express)) {
                            summary = PropertiesUtils.replace(express, v -> {
                                String value = FicoUtil.eval(v, context, null, false, e -> String.format(m, vt.getId(), vte.getId(), v) + e.getMessage());
                                return value == null ? "" : value;
                            });
                        }

                        TempVoucherItem tvi = new TempVoucherItem();
                        tvi.setAccountingOperatingChart(aoc);
                        if (vte.getOrient().equals(AccountingModel.DEBIT)) {
                            tvi.setDebit(amount);
                        } else {
                            tvi.setCredit(amount);
                        }
                        tvi.setSummary(summary);
                        if (!CollectionUtils.isEmpty(tvas)) {
                            tvi.setVoucherAuxiliaryList(tvas);
                            tvi.setCollectAux(collectAux);
                        }
                        tvi.setVoucherTemplateEntry(vte);
                        if (cashFlow != null) {
                            tvi.setCashFlow(cashFlow);
                        }
                        voucherItems.add(tvi);
                        break;
                    }
                });
        return voucherItems;
    }

    /**
     * 查找运营科目
     **/
    private AccountingOperatingChart getAccountingOperatingChart(PartyRole company, AccountingSystem as, VoucherTemplateEntry vte, Map<String, Object> context) {
        AccountingOperatingChart aoc = null;
        /*启用要素科目*/
        if (vte.isEnable()) {
            /*通过要素组查找运营科目*/
            aoc = getAccountingOperatingChartByElementGroup(vte, as, company, context);
        }
        /*未启用要素科目或要素科目无效*/
        if (aoc == null) {
            if (vte.isEnable() && StringUtils.isEmpty(vte.getConditionFormula())) return null;
            aoc = getAccountingOperatingChartByCharCode(company, as, vte.getCharCode());
        }
        if (aoc == null) {
            throw new RuntimeException("公司：" + company.getName() + " 运营科目：" + vte.getCharCode() + "不存在");
        }
        if (!aoc.isFinalFlag()) {
            throw new RuntimeException("公司：" + company.getName() + " 运营科目：" + vte.getCharCode() + "为非末级，不允许结转凭证！");
        }
        return aoc;
    }

    /**
     * 创建辅助核算项目
     **/
    private List<TempVoucherAuxiliary> createTempVoucherAuxiliaries(AccountingOperatingChart aoc, final Map<String, Object> context, VoucherTemplateEntry vte) {
        List<TempVoucherAuxiliary> tvas = new ArrayList<>();
        String auxFormula = vte.getAuxFormula();
        aoc.getAccOperatingChartAuxiliaryList()
                .stream()
                .filter(aoca -> !aoca.getAccAuxiliaryProject().isCashFlow())
                .forEach(aoca -> {
                    Object entity = null;
                    Map<String, Object> ctx = context;
                    while (ctx != null) {
                        if (aoca.getAccAuxiliaryProject().isCashFlow()) break;
                        if (auxFormula == null) {
                            String nodeName = null;
                            if (!FicoUtil.isRootContext(ctx)) nodeName = FicoUtil.getNodeName(ctx);
                            String fieldName = aoca.getAccAuxiliaryProject().commonName(nodeName);
                            if (!StringUtils.isEmpty(nodeName)) fieldName = nodeName + "." + fieldName;
                            entity = FicoUtil.eval(fieldName, ctx, null, false, Throwable::getMessage);
                        } else {
                            List<String> auxFormulas = Arrays.asList(auxFormula.split(","));
                            String auxKeyExpression = auxFormulas.stream().filter(
                                    auxItem -> Arrays.asList(auxItem.split(":")).get(0).equals(aoca.getAccAuxiliaryProject().getId())).findFirst().orElse("无效的辅助核算表达式");
                            if (!auxKeyExpression.contains(":")) {
                                throw new RuntimeException("科目：" + aoc.getCode() + " 辅助核算项目：" + aoca.getAccAuxiliaryProject().getName() + " 扩展表达式配置错误！");
                            }
                            String auxKey = Arrays.asList(auxKeyExpression.split(":")).get(1);
                            entity = FicoUtil.eval(auxKey, ctx, null, false, Throwable::getMessage);
                        }

                        if (entity != null) break;
                        ctx = FicoUtil.getParentContext(ctx);
                    }
                    if (entity == null) {
                        throw new RuntimeException("科目：" + aoc.getCode() + " 辅助核算项目：" + aoca.getAccAuxiliaryProject().getName() + " 值不存在！");
                    }
                    String businessName = null;
                    String businessId;
                    if (entity instanceof ApesBean) {
                        businessId = JpaUtil.getIdToStr((ApesBean) entity);
                        businessName = ReflectUtil.getFieldValue(entity, "name");
                    } else {
                        businessId = (String) entity;
                    }
                    TempVoucherAuxiliary tva = new TempVoucherAuxiliary();
                    tva.setAccountingOperatingChartAuxiliary(aoca);
                    tva.setBusinessField(aoca.getAccAuxiliaryProject().getBusinessField());
                    tva.setCode(businessId);
                    tva.setName(businessName);
                    tvas.add(tva);
                });
        return tvas;
    }

    /**
     * 查询业务凭证
     **/
    private ApesBean getEntity(BusinessDocument document) {
        Class clazz = JpaUtil.getDomainClass(document.getBusinessType());
        if (clazz == null) throw new RuntimeException("业务凭证：'" + document.getBusinessType() + " 错误！");
        CustomRepository<ApesBean, Serializable> repository = JpaUtil.getRepository(clazz.getName());
        assert repository != null;
        return repository.findOne(JpaUtil.getId(document.getBusinessType(), document.getBusinessId()));
    }

    private AccountingSystem getAccountingSystem(PartyRole company) {
        AccountingChartDistributionCompanyService acdcs = SpringManager.getBean(AccountingChartDistributionCompanyService.class);
        assert acdcs != null;
        AccountingSystem as = acdcs.getAccountingSystem(company);
        if (as == null) throw new RuntimeException("公司：" + company.getName() + " 没有定义科目体系！");
        return as;
    }

    private AccountingOperatingChart getAccountingOperatingChartByCharCode(PartyRole company, AccountingSystem as, String charCode) {
        AccountingOperatingChartService aocs = SpringManager.getBean(AccountingOperatingChartService.class);
        assert aocs != null;
        return aocs.findAccountingOperatingChart(company, as, charCode);
    }

    /**
     * 通过要素组查找运营科目
     **/
    private AccountingOperatingChart getAccountingOperatingChartByElementGroup(VoucherTemplateEntry vte, AccountingSystem as, PartyRole company, Map<String, Object> context) {
        ElementChartRepository elementChartRepository = SpringManager.getBean(ElementChartRepository.class);
        assert elementChartRepository != null;
        for (ElementGroupItem egi : vte.getElementGroup().getElementGroupItems()) {
            List<ElementChart> ecs = elementChartRepository.findAll(JpaDsl.toCriteriaByEq("accountingSystem", as, "element", egi.getElement())); /*待优化*/
            for (ElementChart ec : ecs) {
                /*要素值检查*/
                try {
                    Class<?> clazz = Class.forName(ec.getElement().getStrategy());
                    ElementStrategy es = (ElementStrategy) SpringManager.getBean(clazz);
                    if (!es.execute(context, ec)) continue;
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                    continue;
                }

                /*科目检查*/
                AccountingOperatingChart taoc = getAccountingOperatingChartByCharCode(company, as, vte.getCharCode());
                if (taoc == null) continue;
                AccountingOperatingChart aoc = getAccountingOperatingChartByCharCode(company, as, ec.getAccountingChart().getCode());
                if (aoc == null) continue;
                if (!aoc.isFinalFlag()) continue;
                if (isParent(taoc, aoc)) return aoc;
            }
        }
        return null;
    }

    private boolean isParent(AccountingOperatingChart parent, AccountingOperatingChart aoc) {
        return aoc != null && (aoc.getId().equals(parent.getId()) || isParent(parent, aoc.getParent()));
    }
}
